#pragma once
#include <iostream>
#include <queue>

#include <unistd.h>

#include <pthread.h>

#include "lockGuard.hpp"

template<class T>
class CPqueue
{
public:
    // 构造
    CPqueue(int capacity)
        : _capacity(capacity)
    {
        pthread_mutex_init(&_mtx, nullptr);
        pthread_cond_init(&_full, nullptr);
        pthread_cond_init(&_empty, nullptr);
    }

    // 判断队列是否为空
    bool IsEmpty()
    {
        return _q.size() == 0;
    }

    // 判断队列是否已满
    bool IsFull()
    {
        return _q.size() == _capacity;
    }

    // void PushData(const T& data)
    // {
    //     // 进来先上锁
    //     pthread_mutex_lock(&_mtx);

    //     /* 上了锁之后先判断临界资源是
    //     否准备就绪，也就是队列是否满了*/
    //     while(IsFull()) pthread_cond_wait(&_full, &_mtx);

    //     // 到此处就说明队列不满，就可以push数据了
    //     _q.push(data);

    //     // 解锁
    //     pthread_mutex_unlock(&_mtx);

    //     // push完了先发送让消费者消费的信号
    //     pthread_cond_signal(&_empty);
    // }

    // void PopData(T& data)
    // {
    //     // 先上锁
    //     pthread_mutex_lock(&_mtx);

    //     /* 上锁后，先判断临界资源是
    //     否准备就绪，也就是队列是否为空*/
    //     while(IsEmpty()) pthread_cond_wait(&_empty, &_mtx);

    //     // 到此处说明队列不为空，就可以pop了
    //     data = _q.front(); // 先拿数据再pop
    //     _q.pop();

    //     // pop完了发送让生产者生产的信号
    //     pthread_cond_signal(&_full);
        
    //     // 解锁
    //     pthread_mutex_unlock(&_mtx);
    // }

    void PushData(const T& data)
    {
        // 直接让对象来管理锁
        LockGuard LG(&_mtx);
        
        /* 上了锁之后先判断临界资源是
        否准备就绪，也就是队列是否满了*/
        while(IsFull()) pthread_cond_wait(&_full, &_mtx);

        // 到此处就说明队列不满，就可以push数据了
        _q.push(data);

        // 发送消费者消费的信号
        pthread_cond_signal(&_empty);
    }

    void PopData(T& data)
    {
        // 直接让对象来管理锁
        LockGuard LG(&_mtx);

        /* 上锁后，先判断临界资源是
        否准备就绪，也就是队列是否为空*/
        while(IsEmpty()) pthread_cond_wait(&_empty, &_mtx);

        // 到此处说明队列不为空，就可以pop了
        data = _q.front(); // 先拿数据再pop
        _q.pop();

        // pop完了发送让生产者生产的信号
        pthread_cond_signal(&_full);
    }


private:
    // 用队列这个数据结构来表示
    std::queue<T> _q;
    // 超市中能够存放的最大容量
    int _capacity;
    // 判断超市是否已经放满了
    pthread_cond_t _full;
    // 判断超市是否是空的
    pthread_cond_t _empty;
    // 互斥锁，用来两两互斥
    pthread_mutex_t _mtx;
};